package org.cb.core;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import javax.imageio.ImageIO;

import org.cb.types.Font;
import org.cb.types.Letter;

/**
 * 
 * @author Unknown
 * 
 * Stuarts OCR, haven't reviewed yet.
 *
 */
public class OCR {
	String order = "1x28345679Drpoc0eathmnlibdfgkLsjquvwyzABCEFGHIJKMNORPQSTUVWXYZ()-/:";
	private ArrayList<org.cb.types.Font> fonts;
	
	public OCR(ArrayList<Font> theFonts) {
		fonts = theFonts;
	}
	
	public ArrayList<Font> getFonts() {
		return fonts;
	}
	
	public void loadFonts() {
		System.out.println("Loading fonts");
		File mainFolder = new File("Fonts/");
		for( File f : mainFolder.listFiles()){
			System.out.println("Loading Font: " + f.getName());
			Font Font = new Font();
			Font.name = f.getName();
			for(File f2 : f.listFiles()) {
				Letter l = new Letter();
				l.letter = (char) Integer.parseInt(f2.getName().replaceAll(".bmp", ""));
				l.image = trimImageFast(loadCharacter(f.getName(), l.letter));
				Font.letters.add(l);
			}
			fonts.add(Font);
        }
		System.out.println("Loaded fonts");
	}
	
	private BufferedImage loadCharacter(String fontFolder, int decNumber) {
		try {
			return ImageIO.read(new File("Fonts/" + fontFolder + "/" + decNumber + ".bmp"));
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public String getTextAt() {
		return "";
	}
	
	public String getTopText() {
		long time = System.currentTimeMillis();
		BufferedImage source = Global.getTheCanvas().getImage();
		source = getFilteredImage(source, new Rectangle(0, 0, 450, 30));
		try {
			ImageIO.write(source, "png", new File("imgtest.png"));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		ArrayList<Letter> letters = new ArrayList<Letter>();
		Font font = null;
		for(Font f : fonts) {
			if(f.name.equals("UpChars")) font = f;
		}
		if (font == null) {
			System.out.println("UpChars font not found!");
			return "";
		}
		Letter letter = getLetter(source, new Rectangle(0, 0, source.getWidth(), source.getHeight()), font);
		if(letter != null) {
			letters.add(letter);
			int start = (letter.area.x + letter.area.width) + 1;
			int current = 0;
			int end = 50;
			while ((start > 0) && (current < end)) {
				letter = getLetter(source, new Rectangle(start, 0, (source.getWidth() - start), source.getHeight()), font);
				if(letter != null) {
					letters.add(letter);
					start = letter.area.x + letter.area.width;
				} else start = 0;
				current++;
			}
			String message = "";
			for(Letter l : letters) {
				if(l.foundGap) message = message + " " + l.letter;
				else message = message + l.letter;
			}
			message = message.trim();
			System.out.println(System.currentTimeMillis() - time);
			return message;
		}
		return "";
	}
	
	public BufferedImage getFilteredImage(BufferedImage source) {
		return getFilteredImage(source, new Rectangle(0, 0, (int)source.getWidth(), (int)source.getHeight()));
	}
	
	public BufferedImage getFilteredImage(BufferedImage source, Rectangle area) {
		Color white = new Color(228, 228, 227);
		Color yellow = new Color(225, 218, 17);
		Color brown = new Color (201, 125, 44);
		Color green = new Color (74, 218, 25);
		Color shadow = new Color(10, 10, 10);
		for(int x = area.width - 1; x >= area.x; x--) {
			for(int y = area.height - 1; y >= area.y; y--) {
				check: {
					Color col = new Color(source.getRGB(x,y));
					if(col.equals(Color.white)) break check;
					try {
						if(SimilarColors(shadow, col, 60)) {
							Color col2 = new Color(source.getRGB(x - 1, y - 1));
							if(SimilarColors(white, col2, 60) || SimilarColors(brown, col2, 60) || SimilarColors(green, col2, 60) || SimilarColors(yellow, col2, 60)) {
								setPixel(source, x, y, Color.red);
							} else {
								setPixel(source, x, y, Color.black);
							}
						} else {
							setPixel(source, x, y, Color.black);
						}
					} catch(ArrayIndexOutOfBoundsException e) {
						setPixel(source, x, y, Color.black);
					}
				}
			}
		}
		BufferedImage img = new BufferedImage(area.width, area.height, BufferedImage.TYPE_INT_RGB);
		img.getGraphics().drawImage(source, 0, 0, area.width, area.height, area.x, area.y, area.x + area.width, area.y + area.height, null);
		return img;
	}

	private Letter trimImage(BufferedImage img, Rectangle mainarea) {
		try {
			ImageIO.write(img, "png", new File("imgtest.png"));
		}catch (IOException e) {e.printStackTrace();}
		
		Letter letter = new Letter();
		int i;
		int gap = 0;
		for(i = mainarea.x; i < mainarea.x + mainarea.getWidth(); i++) {
			gap++;
			if(colorInLineVertical(img, Color.red, mainarea, i)) break;
		}
		if (gap > 5) letter.foundGap = true;
		if(i == mainarea.x + mainarea.getWidth())
			return null;
		Rectangle area = new Rectangle(i, 0, (int)mainarea.getWidth() - i, (int)mainarea.getHeight());
		int linesEmpty = 0;
		int lastline = 0;
		for(i = area.x + 1; i < area.x + mainarea.getWidth(); i++) {
			if(linesEmpty == 2) break;
			if(colorInLineVertical(img, Color.red, area, i)) {
				lastline = i - area.x;
				linesEmpty = 0;
			} else linesEmpty++;
		}
		if(lastline == area.x) area = new Rectangle(area.x, area.y, 2, (int)mainarea.getHeight());
		else area = new Rectangle(area.x, 0, lastline + 1, (int)mainarea.getHeight());
		if(area.x + area.width == mainarea.width) return null;
		/*
		for(i = mainarea.y; i < mainarea.y + mainarea.getHeight(); i++) {
			if(colorInLineHorizontal(img, Color.red, area, i)) break;
		}
		area = new Rectangle(area.x, i, (int)area.getWidth(), (int)mainarea.getHeight() - i);
		
		linesEmpty = 0;
		lastline = 0;
		for(i = area.y; i < area.y + mainarea.getHeight(); i++) {
			if(linesEmpty == 3) break;
			if(colorInLineHorizontal(img, Color.red, area, i)) { 
				lastline = i;
				linesEmpty = 0;
			} else linesEmpty++;
		}
		if(lastline == area.y) area = new Rectangle(area.x, area.y, (int)area.getWidth(), 2);
		else area = new Rectangle(area.x, area.y, (int)area.getWidth(), (lastline - area.y) + 1);
		//area = new Rectangle(area.x, area.y, (area.width - area.x) + 1, (i - area.y) + 1);
		*/
		BufferedImage letterimg = new BufferedImage(area.width, area.height, BufferedImage.TYPE_INT_RGB);
		letterimg.getGraphics().drawImage(img, 0, 0, area.width, area.height, area.x, area.y, area.x + area.width, area.y + area.height, null);
		letter.area = area;
		letter.image = letterimg;
		return letter;
	}
	
	private BufferedImage trimImageFast(BufferedImage img) {
		int i;
		Color col;
		if(imageHasShadow(img)) col = Color.red;
		else col = Color.white;
		if(col == Color.white) {
			if(!imageHasWhite(img)) return null;
		}
		Rectangle area = new Rectangle(img.getWidth() - 1, img.getHeight() - 1);
		for(i = 0; i < img.getWidth(); i++) {
			if(colorInLineVertical(img, col, area, i)) break;
		}
		area = new Rectangle(i, 0, area.width, area.height);
		for(i = (int)area.getWidth(); i > area.x + 1; i--) {
			if(colorInLineVertical(img, col, area, i)) break;	
		}
		if(i - area.x == 0) area = new Rectangle(area.x, area.y, 1, area.height);
		else area = new Rectangle(area.x, 0, i - area.x, area.height);
		for(i = 0; i < img.getHeight(); i++) {
			if(colorInLineHorizontal(img, col, area, i)) break;
		}
		area = new Rectangle(area.x, i, area.width, area.height);
		for(i = (int)area.getHeight(); i > area.y + 1; i--) {
			if(colorInLineHorizontal(img, col, area, i)) break;	
		}
		if(i - area.y == 0) area = new Rectangle(area.x, area.y, area.width, 1);
		else area = new Rectangle(area.x, area.y, area.width, (i - area.y) + 1);
		BufferedImage image = new BufferedImage(area.width, area.height, BufferedImage.TYPE_INT_RGB);
		image.getGraphics().drawImage(img, 0, 0, area.width, area.height, area.x, area.y, area.x + area.width, area.y + area.height, null);
		removeWhite(image);
		return image;
	}
	
	private boolean imageHasShadow(BufferedImage img) {
		for(int x = 0; x < img.getWidth(); x++) {
			for(int y = 0; y < img.getHeight(); y++) {
				if(new Color(img.getRGB(x, y)).equals(Color.red)) return true;
			}
		}
		return false;
	}
	
	private boolean imageHasWhite(BufferedImage img) {
		for(int x = 0; x < img.getWidth(); x++) {
			for(int y = 0; y < img.getHeight(); y++) {
				if(new Color(img.getRGB(x, y)).equals(Color.white)) return true;
			}
		}
		return false;
	}
	
	private BufferedImage removeWhite(BufferedImage img) {
		for(int x = 0; x < img.getWidth(); x++) {
			for(int y = 0; y < img.getHeight(); y++) {
				if(new Color(img.getRGB(x, y)).equals(Color.white)) {
					setPixel(img, x, y, Color.black);
				}
			}
		}
		return img;
	}
	
	private Letter getLetter(BufferedImage img, Rectangle mainarea, Font f) {
		Letter letter = trimImage(img, mainarea);
		if(letter == null) return null;
		BufferedImage l;
		for(char c : order.toCharArray()) {
			try {
				l = f.findLetter(c).image;
				if(findImage(c, letter.image, l, letter.image.getWidth(), letter.image.getHeight(), 0)) {
					letter.letter = c;
					break;
				}
			} catch(NullPointerException e) {}
		}
		return letter;
		//return null;
	}
	
	private boolean colorInLineVertical(BufferedImage img, Color col, Rectangle area, int line) {
		for (int y = area.y; y < area.y + area.getHeight(); y++) {
			if(SimilarColors(new Color(img.getRGB(line, y)), col, 0)) return true;
		}
		return false;
	}
	
	private boolean colorInLineHorizontal(BufferedImage img, Color col, Rectangle area, int line) {
		for (int x = area.x; x < area.x + area.getWidth(); x++) {
			if(SimilarColors(new Color(img.getRGB(x, line)), col, 0)) return true;
		}
		return false;
	}
	
	private boolean SimilarColors(Color a, Color b, int tolerance) {
		if (Math.abs(a.getRed() - b.getRed()) > tolerance) return false;
		if (Math.abs(a.getGreen() - b.getGreen()) > tolerance) return false;
		if (Math.abs(a.getBlue() - b.getBlue()) > tolerance) return false;
		return true;
	}
	
	private void setPixel (BufferedImage image, int x, int y, Color color ) {
		  Graphics g = image.getGraphics();
		  g.setColor(color);
		  g.fillRect(x, y, 1, 1);
		  g.dispose();
	}
	
	private boolean findImage(char c, BufferedImage orig, BufferedImage img, int x2, int y2, int tolerance) {
		if ((img.getWidth() > orig.getWidth()) || (img.getHeight() > orig.getHeight())) return false;
		for(int currentX = 0; currentX <= (orig.getWidth() - img.getWidth()) + 1; currentX++) {
			for(int currentY = 0; currentY <= (orig.getHeight() - img.getHeight()) + 1; currentY++) {
				if(foundImage(orig, img, currentX, currentY, 0)) {
					return true;
				}
			}
		}
		return false;
	}
	
	private boolean foundImage(BufferedImage orig, BufferedImage img, int x, int y, int tolerance) {
		Color c;
		if ((img.getWidth() > orig.getWidth()) || (img.getHeight() > orig.getHeight())) return false;
		try {
			for(int currentX = 0; currentX <= img.getWidth() - 1; currentX++) {
				for(int currentY = 0; currentY <= img.getHeight() - 1; currentY++) {
					c = new Color(img.getRGB(currentX, currentY));
					if(!c.equals(Color.black)) {
						if(!c.equals(new Color(orig.getRGB(currentX + x, currentY + y))))
							return false;
					}
				}
			}
		} catch(ArrayIndexOutOfBoundsException e) { return false; }
		return true;
	}
}

